﻿using System;
using System.Collections.Generic;
using System.Common;
using System.Controls.Command;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Data;
using System.Windows.Input;

namespace System.Controls
{
  public class SpinEdit : TextBox
  {
    public static readonly DependencyProperty ValueProperty =
      DependencyProperty.Register( "Value",
        typeof( decimal ), typeof( SpinEdit ),
        new FrameworkPropertyMetadata( 0m, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
          null,
          CoerceValueCallbackOfValue ) );

    private static object CoerceValueCallbackOfValue( DependencyObject dependencyObject, object baseValue )
    {
      var value = Converter.ConvertTo<decimal>( baseValue );

      var oMin = dependencyObject.GetValue( MinValueProperty );
      var oMax = dependencyObject.GetValue( MaxValueProperty );

      if ( oMin == null && oMax == null )
        return value;

      if ( oMin != null )
      {
        var min = Converter.ConvertTo<decimal>( oMin );
        if ( value < min )
          return min;
      }

      if ( oMax != null )
      {
        var max = Converter.ConvertTo<decimal>( oMax );
        if ( value > max )
          return max;
      }

      return value;
    }

    public static readonly DependencyProperty MinValueProperty =
      DependencyProperty.Register( "MinValue",
        typeof( decimal? ), typeof( SpinEdit ),
        new PropertyMetadata( ( sender, args ) => ( (SpinEdit) sender ).MinValueChanged() ) );

    public static readonly DependencyProperty MaxValueProperty =
      DependencyProperty.Register( "MaxValue",
        typeof( decimal? ), typeof( SpinEdit ),
        new PropertyMetadata( ( sender, args ) => ( (SpinEdit) sender ).MaxValueChanged() ) );

    public static readonly DependencyProperty IncrementProperty =
      DependencyProperty.Register( "Increment",
        typeof( decimal ), typeof( SpinEdit ) );

    public static readonly DependencyProperty IsFloatValueProperty =
      DependencyProperty.Register( "IsFloatValue",
        typeof( bool ), typeof( SpinEdit ) );

    private ICommand fSpinDownCommand;
    private ICommand fSpinupCommand;

    static SpinEdit()
    {
      DefaultStyleKeyProperty.OverrideMetadata( typeof( SpinEdit ),
        new FrameworkPropertyMetadata( typeof( SpinEdit ) ) );
    }

    public SpinEdit()
    {
      SetBinding( TextProperty, new Binding( "Value" )
                                {
                                  Source = this,
                                  Mode = BindingMode.TwoWay,
                                  UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
                                } );
    }

    public decimal Value
    {
      get { return (decimal) GetValue( ValueProperty ); }
      set { SetValue( ValueProperty, value ); }
    }

    public decimal? MinValue
    {
      get { return (decimal?) GetValue( MinValueProperty ); }
      set { SetValue( MinValueProperty, value ); }
    }

    public decimal? MaxValue
    {
      get { return (decimal?) GetValue( MaxValueProperty ); }
      set { SetValue( MaxValueProperty, value ); }
    }

    public decimal Increment
    {
      get { return (decimal) GetValue( IncrementProperty ); }
      set { SetValue( IncrementProperty, value ); }
    }

    public bool IsFloatValue
    {
      get { return (bool) GetValue( IsFloatValueProperty ); }
      set { SetValue( IsFloatValueProperty, value ); }
    }

    public ICommand SpinUpCommand
    {
      get
      {
        if ( fSpinupCommand != null )
          return fSpinupCommand;

        fSpinupCommand = new DelegateCommand( ExecuteSipUp, CanExecSpinUp );

        return fSpinupCommand;
      }
    }

    public ICommand SpinDownCommand
    {
      get
      {
        if ( fSpinDownCommand != null )
          return fSpinDownCommand;

        fSpinDownCommand = new DelegateCommand( ExecSpinDown, CanExecSpinDown );

        return fSpinDownCommand;
      }
    }

    private void MinValueChanged()
    {
      if ( !MinValue.HasValue )
        return;

      if ( Value < MinValue )
        Value = MinValue.Value;
    }

    private void MaxValueChanged()
    {
      if ( !MaxValue.HasValue )
        return;

      if ( Value > MaxValue )
        Value = MaxValue.Value;
    }

    private bool CanExecSpinUp()
    {
      return ( !MaxValue.HasValue || Value + Increment <= MaxValue ) && Increment != 0;
    }

    private void ExecuteSipUp()
    {
      Value += Increment;
    }

    private bool CanExecSpinDown()
    {
      return ( !MinValue.HasValue || Value - Increment >= MinValue ) && Increment != 0;
    }

    private void ExecSpinDown()
    {
      Value -= Increment;
    }
  }
}
